package jwt

import (
	"errors"
	"strings"
	"time"

	"github.com/gin-gonic/gin"
	jwtpkg "github.com/golang-jwt/jwt/v5"

	"github.com/runningwater/gohub/pkg/app"
	"github.com/runningwater/gohub/pkg/config"
	"github.com/runningwater/gohub/pkg/logger"
)

var (
	ErrTokenExpired           error = errors.New("令牌已过期")
	ErrTokenNotValidYet       error = errors.New("令牌尚未生效")
	ErrTokenMalformed         error = errors.New("令牌格式错误")
	ErrTokenInvalid           error = errors.New("令牌无效")
	ErrTokenExpiredMaxRefresh error = errors.New("令牌已过期，无法刷新")
	ErrHeaderEmpty            error = errors.New("需要认证才能访问！")
	ErrHeaderMalformed        error = errors.New("请求头格式错误")
)

// JWT 结构体，包含签名密钥和刷新 Token 的最大过期时间
type JWT struct {
	// 签名密钥，用于生成和验证 JWT 签名，读取 app.key
	SigningKey []byte

	// 刷新 Token 的最大过期时间
	MaxRefresh time.Duration
}

// CustomClaims 自定义的 JWT 载荷结构体，包含用户 ID、用户名 和过期时间
type CustomClaims struct {
	// 用户 ID
	UserID string `json:"user_id"`
	// 用户名
	UserName string `json:"user_name"`
	// 过期时间
	ExpireTime int64 `json:"expire_time"`

	// 标准的 JWT 载荷字段
	// Audience aud : 受众
	// Issuer iss : 签发者
	// Not Before nbf : 生效时间
	// Issued At iat : 签发时间
	// JWT ID jti : JWT ID 编号
	// Subject sub : 主题
	// Expires At exp : 过期时间
	// 标准的 JWT 载荷字段
	jwtpkg.RegisteredClaims
}

func NewJWT() *JWT {
	return &JWT{
		SigningKey: []byte(config.GetString("app.key")),
		MaxRefresh: time.Duration(config.GetInt64("jwt.max_refresh_time")) * time.Minute,
	}
}

// ParseToken 解析 Token
func (j *JWT) ParseToken(c *gin.Context) (*CustomClaims, error) {

	// 1. 获取请求头中的 Token
	tokenString, err := j.getTokenFromHeader(c)
	if err != nil {
		return nil, err
	}

	// 2. 调用 jwt 库解析用户传入的 token
	token, err := j.parseTokenString(tokenString)

	// 3. 解析出错, 未报错证明是合法的 token
	if err != nil {
		return nil, err
		// validationErr, ok := err.(*jwtpkg.ValidationError)
		// if ok {
		// 	switch validationErr.Errors {
		// 	case jwtpkg.ValidationErrorMalformed:
		// 		return nil, ErrTokenMalformed
		// 	case jwtpkg.ValidationErrorExpired:
		// 		return nil, ErrTokenExpired
		// 	default:
		// 		return nil, ErrTokenInvalid
		// 	}
		// }

		// }
		// return nil, ErrTokenInvalid
	}

	if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
		return claims, nil
	}
	return nil, ErrTokenInvalid

}

// IssueToken 签发 Token
func (j *JWT) IssueToken(userID, userName string) string {

	// 1. 构造用户 claims 信息(负荷)
	expireTime := j.expireAtTime()
	claims := CustomClaims{
		userID,
		userName,
		expireTime.Unix(),
		jwtpkg.RegisteredClaims{
			NotBefore: jwtpkg.NewNumericDate(app.TimenowInTimezone()), // 生效时间
			IssuedAt:  jwtpkg.NewNumericDate(app.TimenowInTimezone()), // 签发时间
			ExpiresAt: jwtpkg.NewNumericDate(expireTime),              // 过期时间
			Issuer:    config.GetString("app.name"),                   // 签发者
			Subject:   userID,                                         // 主题
			Audience:  []string{userName},                             // 受众
		},
	}
	// 2. 根据 claims 生成 Token
	token, err := j.generateToken(claims)
	if err != nil {
		logger.LogIf(err)
		return ""
	}
	return token
}

// RefreshToken 刷新 Token
func (j *JWT) RefreshToken(c *gin.Context) (string, error) {
	// 1. 从 Header 里获取 token
	tokenString, err := j.getTokenFromHeader(c)
	if err != nil {
		return "", err
	}
	// 2. 调用 jwt 库解析用户传入的 token
	token, err := j.parseTokenString(tokenString)
	// 3. 解析出错, 未报错证明是合法的 token
	if err != nil {
		// validationErr, ok := err.(*jwtpkg.ValidationError)
		// // 满足刷新条件：只是单一的报错 ValidationErrorExpired
		// if !ok || validationErr.Errors != jwtpkg.ValidationErrorExpired {
		// 	return "", err
		// }
		if errors.Is(err, jwtpkg.ErrTokenExpired) {
			// 过期了，返回错误
			return "", err
		}
	}

	// 4. 解析 claims
	claims := token.Claims.(*CustomClaims)

	// 5. 判断是否过了最大允许刷新的时间
	x := app.TimenowInTimezone().Add(-j.MaxRefresh).Unix()
	if claims.IssuedAt.Unix() > x {
		// 修改过期时间
		claims.ExpiresAt = jwtpkg.NewNumericDate(j.expireAtTime())
		return j.generateToken(*claims)
	}

	// 6. 超过最大允许刷新的时间，返回错误
	return "", ErrTokenExpiredMaxRefresh
}

// generateToken 生成 Token, 内部使用，外部请调用 IssueToken
func (j *JWT) generateToken(claims CustomClaims) (string, error) {

	// 创建一个新的 Token 对象，使用 HMAC SHA256 签名方法
	token := jwtpkg.NewWithClaims(jwtpkg.SigningMethodHS256, claims)

	return token.SignedString(j.SigningKey)
}

// expireAtTime 计算过期时间
func (j *JWT) expireAtTime() time.Time {
	timeNow := app.TimenowInTimezone()

	var expireTime int64
	if config.GetBool("app.debug") {
		expireTime = config.GetInt64("jwt.debug_expire_time")
	} else {
		expireTime = config.GetInt64("jwt.expire_time")
	}

	expire := time.Duration(expireTime) * time.Minute

	return timeNow.Add(expire)
}

// getTokenFromHeader 获取请求头中的 Token
// Authorization: Bearer Token
// 从请求头中获取 Token，格式为 "Bearer Token"
func (j *JWT) getTokenFromHeader(c *gin.Context) (string, error) {
	// 获取请求头中的 Token
	authHeader := c.Request.Header.Get("Authorization")
	if authHeader == "" {
		return "", ErrHeaderEmpty
	}

	// 检查请求头格式是否正确
	// Bearer Token
	// Bearer 后面有一个空格
	parts := strings.SplitN(authHeader, " ", 2)
	if len(parts) < 2 || parts[0] != "Bearer" {
		return "", ErrHeaderMalformed
	}

	// 返回 Token 字符串
	return parts[1], nil
}

// parseTokenString 解析 Token 字符串
func (j *JWT) parseTokenString(tokenString string) (*jwtpkg.Token, error) {

	return jwtpkg.ParseWithClaims(
		tokenString,
		&CustomClaims{},
		func(token *jwtpkg.Token) (any, error) {
			return j.SigningKey, nil
		},
	)
}
